/*
 * FreeRTOS V202212.00
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */


/*
 * This is a simple example that creates two tasks and one queue.  One task
 * periodically sends a value to the other, which then prints out a message.
 * Normally such a simple example would toggle an LED, so the message that is
 * printed out is "toggle".
 *
 * The demo configures the kernel to be as simple as possible; FreeRTOSConfig.h
 * excludes most features, including dynamic memory allocation.
 */

/* Microchip includes. */
#include "common.h"

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* Priorities at which the tasks are created. */
#define mainQUEUE_RECEIVE_TASK_PRIORITY    ( tskIDLE_PRIORITY + 2 )
#define mainQUEUE_SEND_TASK_PRIORITY       ( tskIDLE_PRIORITY + 1 )

/* The rate at which data is sent to the queue.  The 200ms value is converted
 * to ticks using the portTICK_PERIOD_MS constant. */
#define mainQUEUE_SEND_FREQUENCY_MS        ( pdMS_TO_TICKS( 1000UL ) )

/* The number of items the queue can hold.  This is 1 as the receive task
 * will remove items as they are added, meaning the send task should always find
 * the queue empty. */
#define mainQUEUE_LENGTH_IN_ITEMS          ( 1 )

/*-----------------------------------------------------------*/

/*
 * Configures the clocks ready to run the demo.
 */
static void prvSetupHardware( void );

/*
 * Simple routine to print a string to ITM for viewing in the Keil serial debug
 * viewer.
 */
static void prvITMPrintString( const char * pcString );

/*
 * The tasks as described in the comments at the top of this file.
 */
static void prvQueueReceiveTask( void * pvParameters );
static void prvQueueSendTask( void * pvParameters );

/*-----------------------------------------------------------*/

/* configSUPPORT_STATIC_ALLOCATION is 1 and configSUPPORT_DYNAMIC_ALLOCATION is
 * 0 so the queue structure and the queue storage area can only be statically
 * allocated.  See http://TBD for more information.
 * The queue storage area is dimensioned to hold just one 32-bit value. */
static StaticQueue_t xStaticQueue;
static uint8_t ucQueueStorageArea[ mainQUEUE_LENGTH_IN_ITEMS * sizeof( uint32_t ) ];

/* Holds the handle of the created queue. */
static QueueHandle_t xQueue = NULL;

/* configSUPPORT_STATIC_ALLOCATION is 1 and configSUPPORT_DYNAMIC_ALLOCATION is
 * 0 so the task structure and the stacks used by the tasks can only be statically
 * allocated.  See http://TBD for more information. */
StaticTask_t xRxTCBBuffer, xTxTCBBuffer;
static StackType_t uxRxStackBuffer[ configMINIMAL_STACK_SIZE ], uxTxStackBuffer[ configMINIMAL_STACK_SIZE ];

/*-----------------------------------------------------------*/

int main( void )
{
    /* Set up the hardware ready to run the demo. */
    prvSetupHardware();
    prvITMPrintString( "Starting\r\n" );

    /* Create the queue.  xQueueCreateStatic() has two more parameters than the
     * xQueueCreate() function.  The first new parameter is a pointer to the
     * pre-allocated queue storage area.  The second new parameter is a pointer to
     * the StaticQueue_t structure that will hold the queue state information in
     * an anonymous way. */
    xQueue = xQueueCreateStatic( mainQUEUE_LENGTH_IN_ITEMS, /* The maximum number of items the queue can hold. */
                                 sizeof( uint32_t ),        /* The size of each item. */
                                 ucQueueStorageArea,        /* The buffer used to hold items within the queue. */
                                 &xStaticQueue );           /* The static queue structure that will hold the state of the queue. */

    /* Create the two tasks as described in the comments at the top of this
     * file. */
    xTaskCreateStatic( prvQueueReceiveTask,             /* Function that implements the task. */
                       "Rx",                            /* Human readable name for the task. */
                       configMINIMAL_STACK_SIZE,        /* Task's stack size, in words (not bytes!). */
                       NULL,                            /* Parameter to pass into the task. */
                       mainQUEUE_RECEIVE_TASK_PRIORITY, /* The priority of the task. */
                       &( uxRxStackBuffer[ 0 ] ),       /* The buffer to use as the task's stack. */
                       &xRxTCBBuffer );                 /* The variable that will hold that task's TCB. */

    xTaskCreateStatic( prvQueueSendTask,                /* Function that implements the task. */
                       "Tx",                            /* Human readable name for the task. */
                       configMINIMAL_STACK_SIZE,        /* Task's stack size, in words (not bytes!). */
                       NULL,                            /* Parameter to pass into the task. */
                       mainQUEUE_SEND_TASK_PRIORITY,    /* The priority of the task. */
                       &( uxTxStackBuffer[ 0 ] ),       /* The buffer to use as the task's stack. */
                       &xTxTCBBuffer );                 /* The variable that will hold that task's TCB. */

    /* Start the scheduler. */
    vTaskStartScheduler();

    /* If dynamic memory allocation was used then the following code line would
     * be reached if there was insufficient heap memory available to create either
     * the timer or idle tasks.  As this project is using static memory allocation
     * then the following line should never be reached. */
    for( ; ; )
    {
    }
}
/*-----------------------------------------------------------*/

static void prvQueueSendTask( void * pvParameters )
{
    TickType_t xNextWakeTime;
    const unsigned long ulValueToSend = 100UL;

    /* Initialise xNextWakeTime - this only needs to be done once. */
    xNextWakeTime = xTaskGetTickCount();

    for( ; ; )
    {
        /* Place this task in the blocked state until it is time to run again.
         * The block time is specified in ticks, the constant used converts ticks
         * to ms.  While in the Blocked state this task will not consume any CPU
         * time. */
        vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS );

        /* Send to the queue - causing the queue receive task to unblock and
         * toggle the LED.  0 is used as the block time so the sending operation
         * will not block - it shouldn't need to block as the queue should always
         * be empty at this point in the code. */
        xQueueSend( xQueue, &ulValueToSend, 0U );
    }
}
/*-----------------------------------------------------------*/

static void prvQueueReceiveTask( void * pvParameters )
{
    unsigned long ulReceivedValue;

    for( ; ; )
    {
        /* Wait until something arrives in the queue - this task will block
         * indefinitely provided INCLUDE_vTaskSuspend is set to 1 in
         * FreeRTOSConfig.h. */
        xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY );

        /*  To get here something must have been received from the queue, but
         * is it the expected value?  If it is, toggle the LED. */
        if( ulReceivedValue == 100UL )
        {
            /* Output a string in lieu of using an LED. */
            prvITMPrintString( "Toggle!\r\n" );
        }
    }
}
/*-----------------------------------------------------------*/

static void prvSetupHardware( void )
{
    SystemInit();
    SystemCoreClockUpdate();
}
/*-----------------------------------------------------------*/

void vApplicationStackOverflowHook( TaskHandle_t pxTask,
                                    char * pcTaskName )
{
    /* If configCHECK_FOR_STACK_OVERFLOW is set to either 1 or 2 then this
     * function will automatically get called if a task overflows its stack. */
    ( void ) pxTask;
    ( void ) pcTaskName;

    for( ; ; )
    {
    }
}
/*-----------------------------------------------------------*/

/* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an
 * implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
 * used by the Idle task. */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
                                    StackType_t ** ppxIdleTaskStackBuffer,
                                    uint32_t * pulIdleTaskStackSize )
{
/* If the buffers to be provided to the Idle task are declared inside this
 * function then they must be declared static - otherwise they will be allocated on
 * the stack and so not exists after this function exits. */
    static StaticTask_t xIdleTaskTCB;
    static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];

    /* Pass out a pointer to the StaticTask_t structure in which the Idle task's
     * state will be stored. */
    *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;

    /* Pass out the array that will be used as the Idle task's stack. */
    *ppxIdleTaskStackBuffer = uxIdleTaskStack;

    /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
     * Note that, as the array is necessarily of type StackType_t,
     * configMINIMAL_STACK_SIZE is specified in words, not bytes. */
    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
/*-----------------------------------------------------------*/

/* configUSE_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
 * application must provide an implementation of vApplicationGetTimerTaskMemory()
 * to provide the memory that is used by the Timer service task. */
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
                                     StackType_t ** ppxTimerTaskStackBuffer,
                                     uint32_t * pulTimerTaskStackSize )
{
/* If the buffers to be provided to the Timer task are declared inside this
 * function then they must be declared static - otherwise they will be allocated on
 * the stack and so not exists after this function exits. */
    static StaticTask_t xTimerTaskTCB;
    static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];

    /* Pass out a pointer to the StaticTask_t structure in which the Timer
     * task's state will be stored. */
    *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;

    /* Pass out the array that will be used as the Timer task's stack. */
    *ppxTimerTaskStackBuffer = uxTimerTaskStack;

    /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
     * Note that, as the array is necessarily of type StackType_t,
     * configMINIMAL_STACK_SIZE is specified in words, not bytes. */
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
/*-----------------------------------------------------------*/

static void prvITMPrintString( const char * pcString )
{
    while( *pcString != 0x00 )
    {
        ITM_SendChar( *pcString );
        pcString++;
    }
}
/*-----------------------------------------------------------*/
